package net.wavorizer.control;

import com.google.wave.api.*;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.jdo.PersistenceManager;

import net.wavorizer.util.Utils;
import net.wavorizer.util.PMF;
import net.wavorizer.model.BotAddition;
import net.wavorizer.model.BotRemoval;
import net.wavorizer.model.BotTransaction;

import java.util.logging.Logger;
import java.util.Enumeration;
import java.util.List;
import java.util.Date;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
/*  @TODO:

*/
public class BotController extends AbstractRobotServlet
{
    private static Logger logger = Logger.getLogger(BotController.class.getName());
    private static String BUTTON_ID = "w.BUTTON_ID";
    private static String BOT_MAIN_BLIP_ID = "w.BOT_MAIN_BLIP_ID";
    private static String BARCODE_BLIP_PREFIX = "w.B_B";
    private static String TEXT_AREA_NAME = "w.DATA";

    private String imageRendererURL;
    private String mainBlipTitle;
    private String mainBlipText;

    public void init(ServletConfig config) throws ServletException {
        super.init(config);
        imageRendererURL = Utils.remNull(config.getInitParameter("IMAGE_RENDERER_URL"));
        mainBlipTitle = Utils.remNull(config.getInitParameter("MAIN_BLIP_TITLE"));
        mainBlipText = Utils.remNull(config.getInitParameter("MAIN_BLIP_TEXT"));
        String imageSize = Utils.remNull(config.getInitParameter("IMAGE_SIZE"));

        imageRendererURL = imageRendererURL.replaceAll("_SIZE_", (imageSize.isEmpty() ? "150x150" : imageSize));

        logInit(config, "init()");
    }

    public void processEvents(RobotMessageBundle bundle)
    {
        //if bot was added or new wave with robot participation was created
        if(bundle.wasSelfAdded()){
            addMainBlip(bundle);
        }
        //if bot was removed - delete the main blip
        else if(bundle.wasSelfRemoved()){
            removeMainBlip(bundle);
        }
        else{
            List<Event> events = bundle.getEvents();
            for (Event event : events) {
                switch(event.getType()){
                    case FORM_BUTTON_CLICKED:
                        onFormSubmit(event, bundle);
                        break;
                    case BLIP_DELETED:
                        onMainBlipRemoval(event, bundle);
                        break;
                }
            }
        }
    }

    private void onMainBlipRemoval(Event event, RobotMessageBundle bundle)
    {
        Wavelet w = bundle.getWavelet();
        String removed = Utils.remNull(event.getRemovedBlipId());
        String mainBlipId = Utils.remNull(w.getDataDocument(BOT_MAIN_BLIP_ID));
        logger.info("removed blip: " + removed+" - mainBlipId: " + mainBlipId+" = removed.equals(mainBlipId)==" + removed.equals(mainBlipId));
        if(!removed.isEmpty() && removed.equals(mainBlipId))
        {
            persistBotRemoval(bundle);
            w.setDataDocument(BOT_MAIN_BLIP_ID, null);
            w.removeParticipant(bundle.getRobotAddress());

            logger.info("WAVORIZER was removed from wavelet. ");
        }
    }

    private void onFormSubmit(Event event, RobotMessageBundle bundle)
    {
        Wavelet w = bundle.getWavelet();
        String but = Utils.remNull(event.getButtonName());
        if(!but.equals(BUTTON_ID))
            return;

        logWaveletData(w, "onFormSubmitted()");

        String barcodeCreator = event.getModifiedBy();
        Blip blip = event.getBlip();
        FormElement textarea = blip.getDocument().getFormView().getFormElement(TEXT_AREA_NAME);
        if(textarea == null)
        {
            logger.info("ERROR: textarea " + TEXT_AREA_NAME + " was not found in blip.");
            return;
        }

        String data = textarea.getValue();

        String imgLink = createImgLink(data);
        if(Utils.remNull(imgLink).isEmpty())
            return;

        addBarcodeBlip(w, data, imgLink, barcodeCreator);

        persistBotTransaction(w, event, data);
    }

    private void addBarcodeBlip(Wavelet w, String data, String imgLink, String barcodeCreator)
    {
        Blip blip = w.appendBlip(BARCODE_BLIP_PREFIX + System.currentTimeMillis());
        TextView document = blip.getDocument();
        if(barcodeCreator.isEmpty())
            barcodeCreator = blip.getCreator();
        document.setAuthor(barcodeCreator);
      //  document.appendStyledText(new StyledText("Set your scanner to read barcodes of type \"QR CODE\".", StyleType.HEADING4));
      //  document.appendStyledText(new StyledText(barcodeCreator + " wants you to wavorize: ", StyleType.HEADING4));
        document.appendStyledText(new StyledText(Utils.remNull(data), StyleType.HEADING3));
        document.append("\n");
        document.append("         ");

        Image img = new Image();
        img.setUrl(imgLink);
        img.setCaption("Barcode");
        document.appendElement(img);

        logger.info("Barcode image URL: " + img.getUrl());
    }

    private TextView buildForm(Blip blip)
    {
        TextView document = blip.getDocument();

        //title
        StyledText title = new StyledText(mainBlipTitle, StyleType.HEADING2);
        document.appendStyledText(title);
        document.append("\n");

        //text pieces
        String [] txt = mainBlipText.split("\n", Integer.MAX_VALUE);

        for (String aTxt : txt) {
            aTxt = aTxt.trim();
            if(!aTxt.isEmpty()){
                document.append(aTxt);
                document.append("\n");
            }
        }

        //text area
        Element textArea = new FormElement(ElementType.TEXTAREA, TEXT_AREA_NAME);
        document.appendElement(textArea);

        //submit button
        document.appendElement(new FormElement(
            ElementType.BUTTON,
            BUTTON_ID,
            " SUBMIT "));


        return document;
    }
    private void addMainBlip(RobotMessageBundle bundle)
    {
        persistBotCreation(bundle);
        
        Wavelet wavelet = bundle.getWavelet();
        String wBlipId = Utils.remNull(wavelet.getDataDocument(BOT_MAIN_BLIP_ID));
        if(!wBlipId.isEmpty())
        {
            logger.info("WAVORIZER bot was added to a wave. Main Blip already existed, BOT_MAIN_BLIP_ID: " + wBlipId);
            logWaveletData(wavelet, "addMainBlip()");
            return;
        }

        Blip initBlip = wavelet.appendBlip();
        wavelet.setDataDocument(BOT_MAIN_BLIP_ID, initBlip.getBlipId());

        buildForm(initBlip);

        logWaveletData(wavelet, "addMainBlip()");

        logger.info("WAVORIZER bot was added to a wave. BOT_MAIN_BLIP_ID: " + initBlip.getBlipId());
    }

    private void persistBotCreation(RobotMessageBundle bundle)
    {
        String botCreator = discoverBotContributor(bundle, true);
        Wavelet w = bundle.getWavelet();

        BotAddition add = new BotAddition();
        add.setAddedBy(botCreator);
        add.setCreationDate(new Date());
        add.setParticipants(w.getParticipants());
        add.setWaveId(w.getWaveId());
        add.setWaveletId(w.getWaveletId());

        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            pm.makePersistent(add);
        }
        catch(Exception e)
        {
            logger.info("ERROR:" + e.getMessage());
        }
        finally {
            pm.close();
        }
    }

    private void persistBotRemoval(RobotMessageBundle bundle)
    {
        String botRemover = discoverBotContributor(bundle, false);
        Wavelet w = bundle.getWavelet();

        BotRemoval rem = new BotRemoval();
        rem.setRemovalDate(new Date());
        rem.setRemovedBy(botRemover);
        rem.setWaveId(w.getWaveId());
        rem.setWaveletId(w.getWaveletId());

        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            pm.makePersistent(rem);
        }
        catch(Exception e)
        {
            logger.info("ERROR:" + e.getMessage());
        }
        finally {
            pm.close();
        }
    }

    private void persistBotTransaction(Wavelet w, Event event, String data)
    {
        String barcodeCreator = event.getModifiedBy();

        BotTransaction tx = new BotTransaction();
        tx.setCreatedBy(barcodeCreator);
        tx.setData(data);
        tx.setLinkBody(imageRendererURL);
        tx.setParticipants(w.getParticipants());
        tx.setTransactionDate(new Date());
        tx.setWaveId(w.getWaveId());
        tx.setWaveletId(w.getWaveletId());

        PersistenceManager pm = PMF.get().getPersistenceManager();

        try {
            pm.makePersistent(tx);
        }
        catch(Exception e)
        {
            logger.info("ERROR:" + e.getMessage());
        }
        finally {
            pm.close();
        }
    }

    private String discoverBotContributor(RobotMessageBundle bundle, boolean isAdded)
    {                            
        List<Event> pChanges = bundle.filterEventsByType(isAdded ? EventType.WAVELET_SELF_ADDED : EventType.WAVELET_SELF_REMOVED);
        if(pChanges != null)
        {
            for(Event pChange : pChanges)
            {
                if(pChange != null && !Utils.remNull(pChange.getModifiedBy()).isEmpty())
                        return pChange.getModifiedBy();
            }
        }
        return "UNKNOWN";
    }

    private String createImgLink(String data)
    {
        if(Utils.remNull(data).isEmpty())
            return null;
        //replace \n with spaces to avoid attached strings
        try {
            data = URLEncoder.encode(data.replaceAll("\n", " "), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            logger.info("ERROR: " + e.getMessage());
        }

        return imageRendererURL + data;
    }

    private void removeMainBlip(RobotMessageBundle bundle)
    {
        persistBotRemoval(bundle);
        
        Wavelet w = bundle.getWavelet();
        logWaveletData(w, "removeMainBlip()");

        String initBlipId = Utils.remNull(w.getDataDocument(BOT_MAIN_BLIP_ID));

        if(!initBlipId.isEmpty()){
            Blip root = w.getRootBlip();
            List<Blip> kids = root.getChildren();
            for(int i = 0 ; kids != null && i < kids.size(); i ++){
                Blip _b = kids.get(i);
                if(_b != null && Utils.remNull(_b.getBlipId()).equals(initBlipId)){
                    _b.delete();
                    break;
                }
            }
            w.setDataDocument(BOT_MAIN_BLIP_ID, null);

            logger.info("WAVORIZER bot was removed from a wave. BOT_MAIN_BLIP_ID: " + initBlipId);
        }
        else
            logger.info("WAVORIZER bot was removed from a wave, but BOT_MAIN_BLIP_ID: \"" + initBlipId +"\" was not found.");

    }

    //loggers
    private void logWaveletData(Wavelet w, String methodCall)
    {
        logger.info("=========================");
        logger.info("WAVORIZER LOG METHOD CALL " + Utils.remNull(methodCall));
        logger.info("WAVE ID: " + Utils.remNull(w.getWaveId()));
        logger.info("ID: " + Utils.remNull(w.getWaveletId()));
        logger.info("TITLE: " + Utils.remNull(w.getTitle()));
        logger.info("CREATION TIME: " + w.getCreationTime());
        logger.info("LAST MODIFIED: " + w.getLastModifiedTime());
        logger.info("VERSION: " + w.getVersion());
        logger.info("ROOT BLIP ID: " + w.getRootBlipId());
        logger.info("BOT_MAIN_BLIP_ID: " + w.getDataDocument(BOT_MAIN_BLIP_ID));
        logger.info("PARTICIPANTS: ");
        List<String> p = w.getParticipants();
        for (String aP : p)
            logger.info(aP);
        logger.info("=========================");
    }

    private void logInit(ServletConfig config, String methodCall)
    {
        logger.info("=========================");
        logger.info("WAVORIZER LOG METHOD CALL " + Utils.remNull(methodCall));
        logger.info("VERSION: " + AbstractRobotServlet.getVersion());
        logger.info("CONFIG PARAMS:");
        Enumeration _prms = config.getInitParameterNames();
        while(_prms.hasMoreElements())
        {
            String name = (String) _prms.nextElement();
            String value = Utils.remNull(config.getInitParameter(name));
            logger.info(name +" = " + value);
        }
        logger.info("=========================");
    }

}
